/*
 * AreaDibujo.cpp
 *
 *  Created on: 25/11/2009
 *      Author: luis
 */

#include "AreaDibujo.h"
#include "VentanaNombre.h"
#include "VentanaTiempoTransicion.h"
#include "VentanaInfo.h"
#include "VentanaPrincipal.h"
#include <sstream>

AreaDibujo::AreaDibujo(Circuito* circuito, VentanaPrincipal* ventana_superior):vista_fondo(),
vista_compuerta(circuito), vista_cable(circuito){
	this->circuito_actual = circuito;
	this->ventana_superior = ventana_superior;
	add_events(Gdk::BUTTON_PRESS_MASK);
	add_events(Gdk::BUTTON_RELEASE_MASK);
	add_events(Gdk::POINTER_MOTION_MASK);

	signal_button_release_event().connect(
			sigc::mem_fun(*this, &AreaDibujo::on_eventbox_button_release) );
	signal_button_press_event().connect(
			sigc::mem_fun(*this, &AreaDibujo::on_eventbox_button_press));
	signal_motion_notify_event().connect(
			sigc::mem_fun(*this, &AreaDibujo::on_eventbox_motion_event) );
	estado = INVALIDO;
	estado_anterior= INVALIDO;
	pos_x_aux = INVALIDO;
	pos_y_aux = INVALIDO;
	pos_x_inicial = INVALIDO;
	pos_y_inicial = INVALIDO;

	nro_id=0;
}


bool AreaDibujo::es_posicion_valida(int x, int y){
	bool resultado = true;
	ElementoCircuital* auxiliar = vista_compuerta.get_elemento(x, y);
	if (auxiliar != NULL)
		resultado = false;
	else{
		/*
		 * Si hay un elemento que no permita tirar un cable no sera valida la posicion
		 */
		ElementoCircuital* auxiliar_segundo = vista_compuerta.get_elemento(x-TAMANIO, y);
		if (auxiliar_segundo!=NULL)
			resultado = false;
	}
	return resultado;
}


bool AreaDibujo::hay_otro_elem(int x, int y, ElementoCircuital* elemento){
	ElementoCircuital* aux;
	bool hay_algo = false;
	aux = vista_compuerta.get_compuerta(x, y);
	if (aux==NULL){
		aux = vista_compuerta.get_entrada(x, y);
		if (aux == NULL){
			aux = vista_compuerta.get_salida(x, y);
			if (aux == NULL){
				std::list<ElementoExterno*> lista_elem = circuito_actual->get_lista_elementos_externos();
				std::list<ElementoExterno*>::iterator it = lista_elem.begin();
				unsigned int i;
				ElementoExterno * elem = dynamic_cast<ElementoExterno*>(elemento);
				int ancho;
				int alto;
				elem->calcular_ancho(&ancho, &alto);
				/*
				 * Si hay un elemento donde se hizo click, y no es igual al mismo elemento que se paso por
				 * parametro, devuelve true
				 */
				for (i=0; ((i<lista_elem.size())&&(!hay_algo)); i++){
					if ((((*it)->get_posicion_x()+ancho)>=x)&&(((*it)->get_posicion_x())<=x)){
						if ((((*it)->get_posicion_y()+alto)>=y)&&(((*it)->get_posicion_y())<=y)){
							if ((*it)->get_id()!= elemento->get_id())
								hay_algo = true;
						}
					}
					it++;
				}
			}
			else
				hay_algo = true;
		}
		else
			hay_algo = true;
	}
	return hay_algo;
}


/*
 * Es conexion valida si la entrada no esta ocupada, si no se quiere conectar algo a un elemento
 * circuital del tipo entrada, o si es una conexion a si mismo
 */
bool AreaDibujo::es_conexion_valida(ElementoCircuital* elemento, int nro_entrada, int x, int y){
	bool resultado = true;
	if ((elemento==auxiliar)||(elemento->get_tipo()==TIPO_ENTRADA))
		resultado = false;
	else{
		Salida* salida = vista_compuerta.get_salida(x, y);
		if (salida!=NULL){
			//Si la salida esta lista, es por que tiene la entrada asignada
			if (salida->ready())
				resultado = false;
		}
		else{
			Compuerta* compuerta = vista_compuerta.get_compuerta(x, y);
			if (compuerta!=NULL){
				if (nro_entrada==1){
					if (compuerta->get_entrada1()!=NULL){
						resultado = false;
					}
				}
				else if (nro_entrada == 2){
					if (compuerta->get_entrada2()!=NULL){
						resultado = false;
					}
				}
			}
			else{
				ElementoExterno* externo = vista_compuerta.get_elem_externo(x, y);
				if (externo!=NULL){
					if (externo->entrada_ocupada(nro_entrada))
						resultado = false;
				}
			}
		}

	}
	return resultado;
}


bool AreaDibujo::es_nro_valido(std::string numero){
	unsigned int i;
	bool resultado = true;
	if (numero.empty())
		resultado = false;
	else{
		for (i=0; ((i<numero.length())&&(resultado)); i++){
			if ((ASCII_CERO<=numero[i])&&(numero[i]<=ASCII_NUEVE)){
				resultado = true;
			}
			else
				resultado = false;
		}
	}
	return resultado;
}


bool AreaDibujo::es_derecha(int x, int y){
	bool resultado = false;
	if ((x >= pos_x_inicial + TAMANIO)&&(y < pos_y_inicial + TAMANIO)&&(y > pos_y_inicial - TAMANIO))
		resultado = true;
	else if ((x < pos_x_inicial + TAMANIO)&&(y <= x + (pos_y_inicial - pos_x_inicial))&&(y >= -x + (pos_y_inicial + pos_x_inicial)))
		resultado = true;
	return resultado;
}

bool AreaDibujo::es_izquierda(int x, int y){
	bool resultado = false;
	if ((x <= pos_x_inicial - TAMANIO)&&(y < pos_y_inicial + TAMANIO)&&(y > pos_y_inicial - TAMANIO))
		resultado = true;
	else if ((x > pos_x_inicial - TAMANIO)&&(y <= -x + (pos_y_inicial+pos_x_inicial))&&(y >= x + (pos_y_inicial - pos_x_inicial)))
		resultado = true;
	return resultado;
}

bool AreaDibujo::es_arriba(int x, int y){
	bool resultado = false;
	if ((y <= pos_y_inicial - TAMANIO)&&(x < pos_x_inicial+TAMANIO)&&(x > pos_x_inicial - TAMANIO))
		resultado = true;
	else if ((y > pos_y_inicial - TAMANIO)&&(x <= -y + (pos_y_inicial + pos_x_inicial))&&(x >= y - (pos_y_inicial - pos_x_inicial)))
		resultado = true;
	return resultado;
}

bool AreaDibujo::es_abajo(int x, int y){
	bool resultado = false;
	if ((y >= pos_y_inicial + TAMANIO)&&(x < pos_x_inicial+TAMANIO)&&(x > pos_x_inicial - TAMANIO))
		resultado = true;
	else if ((y < pos_y_inicial + TAMANIO)&&(x <= y - ( pos_y_inicial - pos_x_inicial ))&&(x >= -y + (pos_y_inicial + pos_x_inicial)))
		resultado = true;
	return resultado;
}



void AreaDibujo::agregar_punto_cableado(GdkEventButton* event){
	/*
	 * Si ya hay un cable agregandose, se agrega el punto a el
	 */
	if (estado == CABLEANDO){
		if ((sentido == IZQUIERDA)||(sentido == DERECHA)){
			pos_x_inicial = pos_x_aux;
			this->pos_x_aux = pos_x_inicial;
			this->pos_y_aux = pos_y_inicial;
			this->cuadro_ocupado_x = pos_x_inicial / TAMANIO;
			this->cuadro_ocupado_y = pos_y_inicial / TAMANIO;

		}
		if ((sentido == ARRIBA)||(sentido == ABAJO)){
			pos_y_inicial = pos_y_aux;
			this->pos_x_aux = pos_x_inicial;
			this->pos_y_aux = pos_y_aux;
			this->cuadro_ocupado_x = pos_x_inicial / TAMANIO;
			this->cuadro_ocupado_y = pos_y_inicial / TAMANIO;
		}
		ElementoCircuital* elemento;
		elemento = vista_compuerta.get_elemento(event->x, event->y);
		if (elemento!=NULL){
			int pos_entrada_x = 0;
			int pos_entrada_y = 0;
			int nro_entrada = 1;
			if (elemento->get_tipo()==TIPO_EXTERNO){
				ElementoExterno* aux = (dynamic_cast< ElementoExterno* >(elemento));
				nro_entrada = aux->get_nro_entrada(event->x, event->y);

				pos_entrada_x = aux->get_posicion_entrada_x(nro_entrada);
				pos_entrada_y = aux->get_posicion_entrada_y(nro_entrada);
			}
			else {
				nro_entrada = elemento->get_nro_entrada(event->x, event->y);

				if (nro_entrada == 1){
					pos_entrada_x = elemento->get_pos_entrada1_x();
					pos_entrada_y = elemento->get_pos_entrada1_y();
				}
				else if (nro_entrada == 2){
					pos_entrada_x = elemento->get_pos_entrada2_x();
					pos_entrada_y = elemento->get_pos_entrada2_y();
				}
			}
			if (es_conexion_valida(elemento, nro_entrada, event->x, event->y)){
				Conexion *nueva_conexion = new Conexion (nro_id, auxiliar, nro_entrada, nro_salida, elemento);
				nro_id++;
				elemento->set_entrada(nueva_conexion);
				estado = INVALIDO;
				pos_x_aux = INVALIDO;
				pos_y_aux = INVALIDO;
				this->cable_auxiliar->agregar_punto(pos_entrada_x, pos_entrada_y);
				this->cable_auxiliar->set_id(nro_id-1);
				circuito_actual->agregar_cable(this->cable_auxiliar);
				vista_compuerta.repintar_compuertas(this);
				Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
				vista_cable.pintar_cable_en_contexto(cr);
			}
			else{
				VentanaInfo conexion_invalida("ERROR","Conexion invalida");
				conexion_invalida.run();
				delete cable_auxiliar;
				estado = INVALIDO;
			}
		}
		else {
			this->cable_auxiliar->agregar_punto(pos_x_inicial, pos_y_inicial);
			Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
			vista_cable.pintar_cable_en_contexto(cr);
			vista_cable.repintar_cable_actual(this->cable_auxiliar, this);
			vista_compuerta.repintar_texto(this);
		}
	}
	/*
	 * Si aun no se agrego ningun cable, se agrega el primer punto y se setea el estado como
	 * CABLEANDO
	 */
	else {

		if (vista_compuerta.hay_elementos()){
			ElementoCircuital* elemento;
			elemento = vista_compuerta.get_elemento(event->x, event->y);
			if (elemento!=NULL){
				auxiliar = elemento;
				if (auxiliar->get_tipo()==TIPO_EXTERNO){
					ElementoExterno* aux = dynamic_cast < ElementoExterno* >(auxiliar);
					nro_salida = aux->get_nro_salida(event->x, event->y);
					this->pos_x_inicial = aux->get_posicion_salida_x(nro_salida);
					this->pos_y_inicial = aux->get_posicion_salida_y(nro_salida);
					this->pos_x_aux = pos_x_inicial;
					this->pos_y_aux = pos_y_inicial;
					this->cuadro_ocupado_x = event->x / TAMANIO;
					this->cuadro_ocupado_y = event->y / TAMANIO;
					this->cable_auxiliar = new Cable();
					this->cable_auxiliar->agregar_punto(pos_x_inicial, pos_y_inicial);
					estado = CABLEANDO;
				}
				else if (auxiliar->get_tipo()!=TIPO_SALIDA){
					nro_salida = 1;
					this->pos_x_inicial = auxiliar->get_posicion_salida_x();
					this->pos_y_inicial = auxiliar->get_posicion_salida_y();
					this->pos_x_aux = pos_x_inicial;
					this->pos_y_aux = pos_y_inicial;
					this->cuadro_ocupado_x = event->x / TAMANIO;
					this->cuadro_ocupado_y = event->y / TAMANIO;
					this->cable_auxiliar = new Cable();
					this->cable_auxiliar->agregar_punto(pos_x_inicial, pos_y_inicial);
					estado = CABLEANDO;
				}
				else{
					VentanaInfo error_salida("ERROR","No se puede agregar cable desde una salida");
					error_salida.run();
					estado = INVALIDO;
				}

			}
			else
				estado = INVALIDO;
		}
	}
}


void AreaDibujo::iniciar_traslacion(GdkEventButton* event){
	int x = event->x / TAMANIO;
	int y = event->y / TAMANIO;
	x = TAMANIO * x;
	y = TAMANIO * y;
	this->auxiliar = vista_compuerta.get_elemento(event->x, event->y);
	if (auxiliar!=NULL){
		pos_x_inicial = event->x;
		pos_y_inicial = event->y;
		pos_x_aux = auxiliar->get_posicion_x();
		pos_y_aux = auxiliar->get_posicion_y();
		//Selecciono los cables para mover con el elemento trasladado
		if (auxiliar->get_tipo()==TIPO_EXTERNO){
			circuito_actual->sel_cable_conect_elemexterno(auxiliar);
		}
		else
			circuito_actual->selecc_cables_conectados(x, y);
		pintar_estado_actual();
		vista_fondo.pintar_cuadro(x, y, this);


		estado = TRASLADANDO;
	}
	else
		estado = INVALIDO;
}



void AreaDibujo::rotar(int x, int y){
	ElementoCircuital* auxiliar = vista_compuerta.get_elemento(x, y);
	if (auxiliar!=NULL){
		Cairo::RefPtr<Cairo::Context> cr =get_window()->create_cairo_context();
		if (auxiliar->get_tipo() == TIPO_EXTERNO){
			ElementoExterno* aux = dynamic_cast<ElementoExterno*>(auxiliar);
			circuito_actual->quitar_cables_elem_ext(aux);
			auxiliar->rotar();
			aux->recalcular_dimension();
			aux->liberar_conexiones();
		}
		else{
			auxiliar->rotar();
			circuito_actual->quitar_cables(auxiliar->get_posicion_x(), auxiliar->get_posicion_y());
			auxiliar->liberar_conexiones();
		}
		auxiliar->liberar_entradas();
		vista_fondo.pintar_cuadro(x, y, this);
		vista_compuerta.pintar_compuerta(auxiliar, cr);
		pintar_estado_actual();
	}
}



void AreaDibujo::iniciar_traslacion_masiva(GdkEventButton* event){
	/*
	 * Seteo los enteros auxiliares para comenzar el movimiento
	 */
	int x = event->x;
	int y = event->y;
	int inicio_x;
	int final_x;
	if (pos_x_inicial<pos_x_aux){
		inicio_x = pos_x_inicial;
		final_x = pos_x_aux;
	}
	else{
		inicio_x = pos_x_aux;
		final_x = pos_x_inicial;
	}
	int inicio_y;
	int final_y;
	if (pos_y_inicial<pos_y_aux){
		inicio_y = pos_y_inicial;
		final_y = pos_y_aux;
	}
	else{
		inicio_y = pos_y_aux;
		final_y = pos_y_inicial;
	}
	//Si el click se hizo dentro de la seleccion, se comienza el traslado
	if (((x>inicio_x)&&(x<final_x))&&
		 ((y<final_y)&&(y>inicio_y))){
		estado = TRASLADANDO_SELECCION;
		x = x/TAMANIO;
		x = x*TAMANIO;
		y = y/TAMANIO;
		y = y*TAMANIO;
		this->pos_x_aux = x;
		this->pos_y_aux = y;
	}
	else{
		estado = INVALIDO;
		pintar_estado_actual();
	}

}


void AreaDibujo::borrar_compuerta(GdkEventButton* event){

	ElementoCircuital* elemento = vista_compuerta.get_elemento(event->x, event->y);
	// si hay algo que borrar borro el elemento y sus conexiones
	if (elemento!=NULL){
		if (elemento->get_tipo()==TIPO_EXTERNO){
			ElementoExterno* aux = dynamic_cast<ElementoExterno*>(elemento);
			circuito_actual->quitar_cables_elem_ext(aux);
		}
		else
			circuito_actual->quitar_cables(elemento->get_posicion_x(), elemento->get_posicion_y());
		circuito_actual->borrar_elemento(elemento->get_id());
	}
	else {
		borrar_cable(event->x, event->y);
	}
	pintar_estado_actual();
	estado = INVALIDO;
	estado_anterior = INVALIDO;
}


void AreaDibujo::crear_compuerta(GdkEventButton* event){
	int x, y;
	x = event->x / TAMANIO;
	y = event->y / TAMANIO;
	x = TAMANIO * x;
	y = TAMANIO * y;
	ElementoCircuital* aux = NULL;
	if (es_posicion_valida(event->x, event->y)){
		//Si es conexion valida agrego una compuerta, pidiendole su correspondiente tiempo de transicion
		if ((estado!=IN)&&(estado!=OUT)){
			VentanaTiempoTransicion ventana_nueva;
			int resultado = ventana_nueva.run();
			if (resultado == Gtk::RESPONSE_OK){
				if (es_nro_valido(ventana_nueva.get_tiempo())){
					std::stringstream flujo (ventana_nueva.get_tiempo());
					int k;
					flujo>>k;
					if (estado == COMPUERTA_AND){
						aux = circuito_actual->nueva_and(x, y, nro_id, k);
					}
					if (estado == COMPUERTA_OR){
						aux = circuito_actual->nueva_or(x, y, nro_id, k);
					}
					if (estado == COMPUERTA_NOT){
						aux = circuito_actual->nueva_not(x, y, nro_id, k);
					}
					if (estado == COMPUERTA_XOR){
						aux = circuito_actual->nueva_xor(x, y, nro_id, k);
					}
				}
				else{
					VentanaInfo ventana_error("ERROR", "No es un numero, ingrese uno");
					ventana_error.run();
				}
			}
			else {
				VentanaInfo ventana_ok("ERROR","Apriete OK");
				ventana_ok.run();
			}
		}
		else {
			//Si es una entrada o salida le pido su nombre
			std::string nombre;
			std::string titulo;
			if (estado == IN)
				titulo = "Ingrese nombre de la entrada";
			else
				titulo = "Ingrese nombre de la salida";
			VentanaNombre ventana(titulo);
			int resultado = ventana.run();
			if (resultado == Gtk::RESPONSE_OK){
				if (!ventana.get_nombre().empty()){
					if(ventana.get_nombre().length()<LARGO_NOMBRE){
						if (estado == IN){
							aux = circuito_actual->nueva_entrada(x, y, nro_id, ventana.get_nombre());
						}
						if (estado == OUT){
							aux = circuito_actual->nueva_salida(x, y, nro_id, ventana.get_nombre());
						}
					}
					else{
						VentanaInfo ventana_error("ERROR","Nombre muy largo, ingrese menos de 10 caracteres");
						ventana_error.run();
					}
				}
			}
		}
		if (aux!=NULL){
			Cairo::RefPtr<Cairo::Context> cr =get_window()->create_cairo_context();
			vista_compuerta.repintar_texto(this);
			vista_compuerta.pintar_compuerta(aux, cr);
			nro_id++;
		}
	}
	else{
		VentanaInfo hay_otro_elemento("ERROR","No se puede soltar el elemento en ese lugar");
		hay_otro_elemento.run();
	}
	pintar_estado_actual();
}

void AreaDibujo::terminar_traslacion(GdkEventButton* event){
	circuito_actual->deshacer_seleccion_cables();
	pintar_estado_actual();
	estado = INVALIDO;
}



//Selecciono todos los elementos
void AreaDibujo::seleccionar(int pos_x_inicial,int pos_y_inicial,int pos_x_aux,int pos_y_aux){
	std::list<Compuerta*> lista_compuertas = circuito_actual->get_lista_compuertas();
	std::list<Compuerta*>::iterator it_compuertas = lista_compuertas.begin();
	unsigned int i;
	int x;
	int y;
	for (i=0; i<lista_compuertas.size(); i++){
		for (x=pos_x_inicial; x<pos_x_aux+TAMANIO; x+=TAMANIO){
			for (y=pos_y_inicial; y<pos_y_aux+TAMANIO; y+=TAMANIO){
				if (((*it_compuertas)->get_posicion_x()<=x)&&((*it_compuertas)->get_posicion_x()+TAMANIO>=x)
						&&((*it_compuertas)->get_posicion_y()<=y)&&((*it_compuertas)->get_posicion_y()+TAMANIO>=y)){
					(*it_compuertas)->seleccionar();
				}
			}
		}
		it_compuertas++;
	}

	std::list<Entrada*> lista_entradas = circuito_actual->get_lista_entradas();
	std::list<Entrada*>::iterator it_entradas = lista_entradas.begin();
	for (i=0; i<lista_entradas.size(); i++){
		for (x=pos_x_inicial; x<pos_x_aux+TAMANIO; x+=TAMANIO){
			for (y=pos_y_inicial; y<pos_y_aux+TAMANIO; y+=TAMANIO){
				if (((*it_entradas)->get_posicion_x()<=x)&&((*it_entradas)->get_posicion_x()+TAMANIO>=x)
										&&((*it_entradas)->get_posicion_y()<=y)&&((*it_entradas)->get_posicion_y()+TAMANIO>=y)){
					(*it_entradas)->seleccionar();
				}
			}
		}
		it_entradas++;
	}


	std::list<Salida*> lista_salidas = circuito_actual->get_lista_salidas();
	std::list<Salida*>::iterator it_salidas = lista_salidas.begin();
	for (i=0; i<lista_salidas.size(); i++){
		for (x=pos_x_inicial; x<pos_x_aux+TAMANIO; x+=TAMANIO){
			for (y=pos_y_inicial; y<pos_y_aux+TAMANIO; y+=TAMANIO){
				if (((*it_salidas)->get_posicion_x()<=x)&&((*it_salidas)->get_posicion_x()+TAMANIO>=x)
							&&((*it_salidas)->get_posicion_y()<=y)&&((*it_salidas)->get_posicion_y()+TAMANIO>=y)){
					(*it_salidas)->seleccionar();
				}
			}
		}
		it_salidas++;
	}


	std::list<ElementoExterno*> lista_elem = circuito_actual->get_lista_elementos_externos();
	std::list<ElementoExterno*>::iterator it_elem = lista_elem.begin();
	bool seleccionado = false;
	for (i=0; i<lista_elem.size(); i++){
		int ancho, alto;
		(*it_elem)->calcular_ancho(&ancho, &alto);
		for (x=pos_x_inicial; ((x<pos_x_aux+TAMANIO)&&(!seleccionado)); x+=TAMANIO){
			for (y=pos_y_inicial; ((y<pos_y_aux+TAMANIO)&&(!seleccionado)); y+=TAMANIO){
				if (((*it_elem)->get_posicion_x() <= x)&&((*it_elem)->get_posicion_x()+ancho >= x)
						&&((*it_elem)->get_posicion_y()<=y)&&((*it_elem)->get_posicion_y()+alto>=y)){
					(*it_elem)->seleccionar();
					seleccionado = true;
				}
			}
		}
		seleccionado = false;

		it_elem++;
	}

	sel_cables_mov_extremo();

}


void AreaDibujo::deshacer_seleccion(){
	std::list<Compuerta*> lista_compuertas = circuito_actual->get_lista_compuertas();
	std::list<Compuerta*>::iterator it_compuertas = lista_compuertas.begin();
	unsigned int i;
	for (i=0; i<lista_compuertas.size(); i++){
		if ((*it_compuertas)->is_seleccionado())
			(*it_compuertas)->deshacer_seleccion();
		it_compuertas++;
	}

	std::list<Entrada*> lista_entradas = circuito_actual->get_lista_entradas();
	std::list<Entrada*>::iterator it_entradas = lista_entradas.begin();
	for (i=0; i<lista_entradas.size(); i++){
		if ((*it_entradas)->is_seleccionado())
			(*it_entradas)->deshacer_seleccion();
		it_entradas++;
	}


	std::list<Salida*> lista_salidas = circuito_actual->get_lista_salidas();
	std::list<Salida*>::iterator it_salidas = lista_salidas.begin();
	for (i=0; i<lista_salidas.size(); i++){
		if ((*it_salidas)->is_seleccionado())
			(*it_salidas)->deshacer_seleccion();
		it_salidas++;
	}

	std::list<ElementoExterno*> lista_elem = circuito_actual->get_lista_elementos_externos();
	std::list<ElementoExterno*>::iterator it_elem = lista_elem.begin();
	for (i=0; i<lista_elem.size(); i++){
		if ((*it_elem)->is_seleccionado())
			(*it_elem)->deshacer_seleccion();
		it_elem++;
	}


	std::list<Cable*> lista_cables = circuito_actual->get_lista_cables();
	std::list<Cable*>::iterator it_cables = lista_cables.begin();

	for (i=0; i< lista_cables.size(); i++){
		if (((*it_cables)->is_seleccionado())||((*it_cables)->is_moviendo_extremo()))
			(*it_cables)->deshacer_seleccion();
		it_cables++;
	}

}


void AreaDibujo::repintar_movimiento_elem(GdkEventMotion* event){
	int x = (event->x / TAMANIO);
	x = x*TAMANIO;
	int y = (event->y / TAMANIO);
	y = y*TAMANIO;
	get_window()->invalidate(true);
	Cairo::RefPtr<Cairo::Context> cr =get_window()->create_cairo_context();
	auxiliar->set_posicionX(x);
	auxiliar->set_posicionY(y);
	circuito_actual->recalcular_pos_rel_cables(pos_x_aux, pos_y_aux, x, y);
	vista_cable.pintar_cable_en_contexto(cr);
	vista_compuerta.pintar_compuerta(auxiliar, cr);
	pos_x_aux = x;
	pos_y_aux = y;
}



void AreaDibujo::repintar_traslacion_masiva(GdkEventMotion* event){
	int x = (event->x / TAMANIO);
	x = x*TAMANIO;
	int y = (event->y / TAMANIO);
	y = y*TAMANIO;
	get_window()->invalidate(true);
	Cairo::RefPtr<Cairo::Context> cr =get_window()->create_cairo_context();

	std::list<Compuerta*> lista_compuertas = circuito_actual->get_lista_compuertas();
	std::list<Compuerta*>::iterator it_compuertas = lista_compuertas.begin();
	unsigned int i;
	for (i=0; i<lista_compuertas.size(); i++){
		if ((*it_compuertas)->is_seleccionado()){
			(*it_compuertas)->set_posicionX_rel(pos_x_aux, x);
			(*it_compuertas)->set_posicionY_rel(pos_y_aux, y);
		}
		it_compuertas++;
	}
	std::list<Entrada*> lista_entradas = circuito_actual->get_lista_entradas();
	std::list<Entrada*>::iterator it_entradas = lista_entradas.begin();
	for (i=0; i<lista_entradas.size(); i++){
		if ((*it_entradas)->is_seleccionado()){
			(*it_entradas)->set_posicionX_rel(pos_x_aux, x);
			(*it_entradas)->set_posicionY_rel(pos_y_aux, y);
		}
		it_entradas++;
	}

	std::list<Salida*> lista_salidas = circuito_actual->get_lista_salidas();
	std::list<Salida*>::iterator it_salidas = lista_salidas.begin();
	for (i=0; i<lista_salidas.size(); i++){
		if ((*it_salidas)->is_seleccionado()){
			(*it_salidas)->set_posicionX_rel(pos_x_aux, x);
			(*it_salidas)->set_posicionY_rel(pos_y_aux, y);
		}
		it_salidas++;
	}

	std::list<ElementoExterno*> lista_elem = circuito_actual->get_lista_elementos_externos();
	std::list<ElementoExterno*>::iterator it_elem = lista_elem.begin();
	for (i=0; i<lista_elem.size(); i++){
		if ((*it_elem)->is_seleccionado()){
			(*it_elem)->set_posicionX_rel(pos_x_aux, x);
			(*it_elem)->set_posicionY_rel(pos_y_aux, y);
		}
		it_elem++;
	}


	std::list<Cable*> lista_cable = circuito_actual->get_lista_cables();
	std::list<Cable*>::iterator it_cable = lista_cable.begin();
	for (i=0; i<lista_cable.size(); i++){
		if ((*it_cable)->is_seleccionado()){
			(*it_cable)->trasladar_x_rel(pos_x_aux, x);
			(*it_cable)->trasladar_y_rel(pos_y_aux, y);
		}
		else if ((*it_cable)->is_moviendo_extremo()){
			(*it_cable)->recalcular_camino_relativo(pos_x_aux, pos_y_aux, x, y);
		}
		it_cable++;
	}

	pos_x_aux = x;
	pos_y_aux = y;
	vista_cable.pintar_cable_en_contexto(cr);
	vista_compuerta.repintar_compuertas(this);
}


void AreaDibujo::repintar_cableado(GdkEventMotion* event){
    int x = event->x;
    int y = event->y;
    actualizar_situacion(x, y);
    pintar_linea();
    pos_x_aux = x;
    pos_y_aux = y;
}


void AreaDibujo::terminar_cableado(GdkEventButton* event){
	estado = INVALIDO;
}


void AreaDibujo::actualizar_situacion(int x, int y){
	bool mov_correcto = false;
	get_window()->invalidate(true);
	if (es_derecha(x, y)){
		sentido = DERECHA;
		mov_correcto = true;
	}
	else if (es_izquierda(x, y)){
		sentido = IZQUIERDA;
		mov_correcto = true;
	}
	else if (es_abajo(x, y)){
		sentido = ABAJO;
		mov_correcto = true;
	}
	else if (es_arriba(x, y)){
		sentido = ARRIBA;
		mov_correcto = true;
	}

	if (mov_correcto){
		if (x > (cuadro_ocupado_x+1)*50)
			cuadro_ocupado_x += 1;
		if (x < (cuadro_ocupado_x-1)*50)
			cuadro_ocupado_x -= 1;
		if (y > (cuadro_ocupado_y+1)*50)
			cuadro_ocupado_y += 1;
		if (y < (cuadro_ocupado_y-1)*50)
			cuadro_ocupado_y -= 1;
	}
}


void AreaDibujo::pintar_linea(){
	vista_cable.repintar_cable_actual(this->cable_auxiliar, this);
	if ((sentido == IZQUIERDA)||(sentido == DERECHA))
		vista_cable.pintar_cable(pos_x_inicial, pos_y_inicial, pos_x_aux, pos_y_inicial, this);
	if ((sentido == ARRIBA)||(sentido == ABAJO))
		vista_cable.pintar_cable(pos_x_inicial, pos_y_inicial, pos_x_inicial, pos_y_aux, this);
}


void AreaDibujo::borrar_cable(float x, float y){
	int inicio_x;
	int inicio_y;
	Cable* cable = vista_cable.get_cable(x, y, &inicio_x, &inicio_y);
	if (cable!=NULL){
		ElementoCircuital* elemento = vista_compuerta.get_elemento(inicio_x, inicio_y);
		elemento->cortar_cable(cable->get_id());
		circuito_actual->sacar_cable(cable);
	}
}


void AreaDibujo::sel_cables_mov_extremo(){
	std::list<Cable*> lista_cables = circuito_actual->get_lista_cables();
	std::list<Cable*>::iterator it_cables = lista_cables.begin();

	std::list<PuntoQuiebre*> lista_quiebres;
	std::list<PuntoQuiebre*>::iterator inicio;
	std::list<PuntoQuiebre*>::iterator final;
	unsigned int i;
	ElementoCircuital * auxiliar_inicial;
	ElementoCircuital * auxiliar_final;
	int x_inicial;
	int y_inicial;
	int x_final;
	int y_final;
	for (i=0; i< lista_cables.size(); i++){
		lista_quiebres = (*it_cables)->get_lista();
		inicio = lista_quiebres.begin();
		final = lista_quiebres.end();
		final--;
		x_inicial = (*inicio)->get_x()/TAMANIO;
		x_inicial = x_inicial*TAMANIO;
		y_inicial = (*inicio)->get_y()/TAMANIO;
		y_inicial = y_inicial*TAMANIO;

		x_final = (*final)->get_x()/TAMANIO;
		x_final = x_final*TAMANIO;
		y_final = (*final)->get_y()/TAMANIO;
		y_final = y_final*TAMANIO;

		auxiliar_inicial = vista_compuerta.get_elemento(x_inicial, y_inicial);
		if (auxiliar_inicial!=NULL){
			if (auxiliar_inicial->is_seleccionado()){
				auxiliar_final = vista_compuerta.get_elemento(x_final, y_final);
				if (auxiliar_final!=NULL){
					if (auxiliar_final->is_seleccionado())
						(*it_cables)->seleccionar();
					else{
						(*it_cables)->moviendo_inicio();
					}
				}
			}
			else{
				ElementoCircuital* auxiliar_final_2 = vista_compuerta.get_elemento(x_final, y_final);
				if (auxiliar_final_2!=NULL){
					if (auxiliar_final_2->is_seleccionado()){
						(*it_cables)->moviendo_fin();
					}
				}
			}
		}
		it_cables++;
	}
}

void AreaDibujo::actualizar_estado(){
	if (estado_anterior==SELECCIONANDO){
		deshacer_seleccion();
		pintar_estado_actual();
	}
}

void AreaDibujo::pintar_estado_actual(){
	vista_fondo.pintar_fondo(this);
	vista_compuerta.repintar_compuertas(this);
	vista_compuerta.repintar_texto(this);
	Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
	vista_cable.pintar_cable_en_contexto(cr);
}

void AreaDibujo::set_estado(int estado){
	this->estado = estado;
}


void AreaDibujo::borrar_seleccion(){
	std::list<Compuerta*> lista_compuertas = circuito_actual->get_lista_compuertas();
	std::list<Compuerta*>::iterator it_compuertas = lista_compuertas.begin();
	unsigned int i;
	for (i=0; i<lista_compuertas.size(); i++){
		if ((*it_compuertas)->is_seleccionado()){
			circuito_actual->borrar_elemento((*it_compuertas)->get_id());
			circuito_actual->quitar_cables((*it_compuertas)->get_posicion_x(), (*it_compuertas)->get_posicion_y());
		}
		it_compuertas++;
	}

	std::list<Entrada*> lista_entradas = circuito_actual->get_lista_entradas();
	std::list<Entrada*>::iterator it_entradas = lista_entradas.begin();
	for (i=0; i<lista_entradas.size(); i++){
		if ((*it_entradas)->is_seleccionado()){
			circuito_actual->borrar_elemento((*it_entradas)->get_id());
			circuito_actual->quitar_cables((*it_entradas)->get_posicion_x(), (*it_entradas)->get_posicion_y());
		}
		it_entradas++;
	}


	std::list<Salida*> lista_salidas = circuito_actual->get_lista_salidas();
	std::list<Salida*>::iterator it_salidas = lista_salidas.begin();
	for (i=0; i<lista_salidas.size(); i++){
		if ((*it_salidas)->is_seleccionado()){
			circuito_actual->borrar_elemento((*it_salidas)->get_id());
			circuito_actual->quitar_cables((*it_salidas)->get_posicion_x(), (*it_salidas)->get_posicion_y());
		}
		it_salidas++;

	}
	std::list<ElementoExterno*> lista_elem = circuito_actual->get_lista_elementos_externos();
	std::list<ElementoExterno*>::iterator it_elem = lista_elem.begin();
	for (i=0; i<lista_elem.size(); i++){
		if ((*it_elem)->is_seleccionado()){
			circuito_actual->quitar_cables_elem_ext(*it_elem);
			circuito_actual->borrar_elemento((*it_elem)->get_id());
		}
		it_elem++;
	}
	pintar_estado_actual();
	estado_anterior = INVALIDO;
}

void AreaDibujo::simular(){
	if (circuito_actual->circuito_listo()){
		try{
			std::string string_salida = circuito_actual->simular();
			if (string_salida!="-1"){
				VentanaSalida salida(circuito_actual, string_salida);
				salida.run();
			}
			else {
				VentanaInfo no_hubo_simulacion("CIRCUITO NO SIMULADO", "Circuito no valido para simular");
				no_hubo_simulacion.run();
			}
		}
		catch (NoSimulacionExcepcion){
			VentanaInfo circuito_desconectado("ERROR", "Circuito externo desconectado");
			circuito_desconectado.run();
		}
	}
	else{
		VentanaInfo no_listo("ERROR","Circuito no listo");
		no_listo.run();
	}
}

void AreaDibujo::pintar_en_contexto(Cairo::RefPtr<Cairo::Context> cr, float x, float y){
	cr->save();
	float ancho = get_width();
	float alto = get_height();
	float x_scala = x / ancho;
	float y_scala = y / (alto);
	cr->scale(x_scala, y_scala);

	vista_fondo.pintar_fondo_en_contexto(this, cr);
	vista_compuerta.pintar_en_contexto(this, cr);


	vista_cable.pintar_cable_en_contexto(cr);

	cr->restore();
}

bool AreaDibujo::on_expose_event(GdkEventExpose *event){
	pintar_estado_actual();
    return true;
}


bool AreaDibujo::on_eventbox_motion_event(GdkEventMotion * event){
	if (estado == TRASLADANDO){
		repintar_movimiento_elem(event);
	}
	if (estado == TRASLADANDO_SELECCION){
		repintar_traslacion_masiva(event);
	}
	if (estado == CABLEANDO){
		repintar_cableado(event);
	}
	if (estado == AGREGANDO_CAJA){
		int x = event->x/TAMANIO;
		x = x*TAMANIO;
		int y = event->y/TAMANIO;
		y = y*TAMANIO;

		auxiliar->set_posicionX(x);
		auxiliar->set_posicionY(y);
		get_window()->invalidate(false);
		Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
		vista_compuerta.pintar_compuerta(auxiliar, cr);
	}
    return true;
}

bool AreaDibujo::on_eventbox_button_press(GdkEventButton* event){

	if (event->type == GDK_2BUTTON_PRESS){
		ElementoExterno* elemento_actual;
		if ((elemento_actual=vista_compuerta.get_elem_externo(event->x, event->y))!=NULL){
			std::string ip = elemento_actual->get_ip();
			int puerto = elemento_actual->get_puerto();
			std::string nombre = elemento_actual->get_nombre();
			char * rutaPrueba = elemento_actual->get_cliente()->mostrar_circuito(ip, puerto, nombre);
			if (rutaPrueba!=NULL){
				if ((this->ventana_superior->abrir_externo(rutaPrueba))==CORRECTO)
					this->ventana_superior->externo_abierto();
			}
			else{
				VentanaInfo circuito_no_disponible("ERROR AL ABRIR CIRCUITO EXTERNO", "El circuito no esta disponible en este momento");
				circuito_no_disponible.run();
			}

		}
		estado = INVALIDO;
		estado_anterior=INVALIDO;
	}
	if (estado == ROTANDO){

		rotar (event->x, event->y);
	}


	if (estado == SEL_TRASLAD){
		iniciar_traslacion(event);
	}
	if (estado == TRASLADAR_SELECCION){
		iniciar_traslacion_masiva(event);
	}
	if (estado == SELECCIONANDO){
		int x = (event->x/TAMANIO);
		x = x*TAMANIO;
		int y = (event->y/TAMANIO);
		y = y*TAMANIO;
		pos_x_inicial = x;
		pos_y_inicial = y;

	}

	if (estado == CABLEANDO)
		agregar_punto_cableado(event);
	if (estado == INICIANDO_CABLEADO){
		agregar_punto_cableado(event);
	}
	if (estado == BORRANDO){
		borrar_compuerta(event);
	}
	return true;
}

bool AreaDibujo::on_eventbox_button_release(GdkEventButton* event){
	if (estado == INVALIDO){
		if (estado_anterior==SELECCIONANDO){
			actualizar_estado();
		}
	}
	if ((estado == COMPUERTA_AND)||(estado == COMPUERTA_OR)||(estado == COMPUERTA_XOR)||
			(estado == COMPUERTA_NOT)||(estado == OUT)||(estado == IN))
	{
		int x = event->x/TAMANIO;
		x = x*TAMANIO;
		int y = event->y/TAMANIO;
		y = y*TAMANIO;
		vista_compuerta.pintar_compuerta(x,y, this, estado);
		crear_compuerta(event);
		estado = INVALIDO;
		pos_x_aux = INVALIDO;
		pos_y_aux = INVALIDO;

	}
	if (estado == AGREGANDO_CAJA){
		int x = (event->x/TAMANIO);
		x = x*TAMANIO;
		int y = (event->y/TAMANIO);
		y = y*TAMANIO;
		Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
		if (!hay_otro_elem(event->x, event->y, auxiliar)){
			auxiliar->set_posicionX(x);
			auxiliar->set_posicionY(y);
			vista_compuerta.pintar_compuerta(auxiliar, cr);
			nro_id++;
			estado = INVALIDO;
		}
		else{
			VentanaInfo posicion_invalida ("ERROR", "Posicion invalida, posicione en otro lugar el elemento");
			posicion_invalida.run();
		}
	}
	if (estado == TRASLADANDO){

		terminar_traslacion(event);
	}
	if (estado == SELECCIONANDO){
		int x = (event->x/TAMANIO);
		x = x*TAMANIO;
		int y = (event->y/TAMANIO);
		y = y*TAMANIO;
		pos_x_aux = x;
		pos_y_aux = y;
		vista_fondo.pintar_seleccion(pos_x_inicial, pos_y_inicial, event->x, event->y, this);
		vista_compuerta.repintar_compuertas(this);
		vista_compuerta.repintar_texto(this);
		Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context();
		vista_cable.pintar_cable_en_contexto(cr);
		estado_anterior = SELECCIONANDO;
		int inicio_x;
		int final_x;
		if (pos_x_inicial<pos_x_aux){
			inicio_x = pos_x_inicial;
			final_x = pos_x_aux;
		}
		else{
			inicio_x = pos_x_aux;
			final_x = pos_x_inicial;
		}
		int inicio_y;
		int final_y;
		if (pos_y_inicial<pos_y_aux){
			inicio_y = pos_y_inicial;
			final_y = pos_y_aux;
		}
		else{
			inicio_y = pos_y_aux;
			final_y = pos_y_inicial;
		}
		seleccionar (inicio_x, inicio_y, final_x, final_y);
		estado = INVALIDO;
	}
	if (estado == TRASLADANDO_SELECCION){
		estado = INVALIDO;
		estado_anterior = INVALIDO;
		deshacer_seleccion();
	}
	return true;
}



AreaDibujo::~AreaDibujo(){

}
